# !/usr/bin/env python
# -*-coding:utf-8 -*-

"""
# Author     ：xialang333@gmail.com
# Time       ：2024/4/13 10:06
# Description：
"""
import os
import sys
import time
import json
import uuid
import platform
import subprocess

from io import StringIO
from threading import Semaphore
from application.settings import BASE_DIR
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore
from django_apscheduler.models import DjangoJobExecution
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR, EVENT_JOB_ADDED
from utils.jsonResponse import DetailResponse, SuccessResponse, ErrorResponse
from utils.serializers import CustomModelSerializer
from utils.viewset import CustomModelViewSet
from apps.crond.models import Task, TaskMethod, TaskResult
from rest_framework.decorators import action

# def task_listener(event):
    
#     if event.code == EVENT_JOB_EXECUTED:
#         output_text = sys.stdout.getvalue()
#         print(output_text)
#         print('任务执行成功')
#     if event.code == EVENT_JOB_ERROR:
#         print('任务执行错误')
#     if event.code == EVENT_JOB_ADDED:
#         print('任务添加成功')

scheduler = BackgroundScheduler()
# scheduler.add_listener(
#     callback=task_listener,
#     mask=EVENT_JOB_ERROR | EVENT_JOB_ADDED | EVENT_JOB_EXECUTED
# )
# 创建调度器
# 创建调度器
scheduler.add_jobstore(DjangoJobStore(), 'default')
scheduler.start()
# 删除任务
# scheduler.remove_job(job_name)
# 暂停任务
# scheduler.pause_job(job_name)
# 开启任务
# scheduler.resume_job(job_name)
# 修改任务
# scheduler.modify_job(job_name)

# 获取tasks任务
def get_job_list():
    from application import settings
    tasks_list = []
    tasks_dict_list = []
    method_obj = TaskMethod.objects.all()
    for method_data in method_obj:
        task_dict = dict()
        task_dict['label'] = method_data.name
        task_dict['value'] = method_data.id
        tasks_dict_list.append(task_dict)
#     for app in settings.INSTALLED_APPS:
#         try:
#             exec(f"""
# from {app} import tasks
# for ele in [i for i in dir(tasks) if not i.startswith('__')]:
#     task_dict = dict()
#     task_dict['label']="{app}.tasks." + ele
#     task_dict['value']="{app}.tasks." + ele
#     tasks_list.append("{app}.tasks." + ele)
#     tasks_dict_list.append(task_dict)
#                             """)
#         except ImportError:
#             pass
    return {"tasks_list": tasks_list, "tasks_dict_list": tasks_dict_list}



def task_method_shell(data, job__id):
    
    current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    try:
        env = os.environ.copy()
        script_tmp = os.path.join(BASE_DIR, "script")
        if " " in data:
            fileName = data.split(" ")[0]
            file = os.path.join(script_tmp, fileName)
            tmp_cmd = [file] + data.split(" ")[1:] 
        else:
            file = os.path.join(script_tmp, data)
            tmp_cmd = [file]

        if os.path.exists(file):
            res = subprocess.run(tmp_cmd, shell=False, env=env, capture_output=True, text=True)
            result = res.stdout 
            TaskResult.objects.create(job_id=job__id, status="执行成功", run_time=current_time, result=result)
        else:
            result = "脚本文件不存在"
            TaskResult.objects.create(job_id=job__id, status="执行失败", run_time=current_time, result=result)
    except Exception as e:
        TaskResult.objects.create(job_id=job__id, status="执行失败", run_time=current_time, result=str(e))


def task_method_python(data, job_id, **kwargs):
    current_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    try:
        env = os.environ.copy()
        script_tmp = os.path.join(BASE_DIR, "script")
        if " " in data:
            fileName = data.split(" ")[0]
            file = os.path.join(script_tmp, fileName)
            tmp_cmd = ['python3', file] + data.split(" ")[1:] 
        else:
            fileName = data.split(" ")[0]
            file = os.path.join(script_tmp, fileName)
            tmp_cmd = ['python3', file]
        if os.path.exists(file):
            res = subprocess.run(tmp_cmd, shell=False, env=env, capture_output=True, text=True)
            result = res.stdout 
            TaskResult.objects.create(job_id=job_id, status="执行成功", run_time=current_time, result=result)
        else:
            result = "脚本文件不存在"
            TaskResult.objects.create(job_id=job_id, status="执行失败", run_time=current_time, result=result)
    except Exception as e:
        TaskResult.objects.create(job_id=job_id, status="执行失败", run_time=current_time, result=str(e))

class TaskHandler:
    def __init__(self, data) -> None:
        self.task_method = data["job"]
        self.job_id = uuid.uuid4()
        self.job_type = data["type"]
        self.data = data
    
    def cron_str(self):
        cron_str = None
        cron_meta = None
        if self.job_type == 1:
            cron_meta = self.data["crontab"].split(" ")
            cron_meta = self.data["crontab"]
            cron_str = "\"cron\","
            if cron_meta[0] != "*":
                cron_str = cron_str + f"minute=\"{cron_meta[0]}\""
            if cron_meta[1] != "*":
                cron_str = cron_str + f"hour=\"{cron_meta[1]}\""
            if cron_meta[2] != "*":
                cron_str = cron_str + f"day=\"{cron_meta[2]}\""
            if cron_meta[3] != "*":
                cron_str = cron_str + f"month=\"{cron_meta[3]}\""
            if cron_meta[4] != "*":
                cron_str = cron_str + f"week=\"{cron_meta[4]}\""
        elif self.job_type == 0:
            
            period = self.data["interval"]["period"]
            every = self.data["interval"]["every"]
            cron_str = f"\"interval\",{period}={every}"
            if period == "days":
                cron_meta = f"* * */{str(every)} * * "
            if period == "hours":
                cron_meta = f"* */{str(every)} * * * "
            if period == "minutes":
                cron_meta = f"*/{str(every)} * * * * "
        return cron_str, cron_meta

    def task_data(self):
        
        task_data_obj = TaskMethod.objects.filter(id=self.task_method).first()
        task_exec_type = task_data_obj.language_type
        script_tmp = os.path.join(BASE_DIR, "script")
        if task_exec_type == "shell" and platform.system() != "Windows":
            msg = self.shell_hand(task_data_obj,script_tmp)
            return {"code": 200,"msg":msg}
        elif task_exec_type == "python":
            msg = self.python_hand(task_data_obj,script_tmp)
            return {"code": 200,"msg":msg}
        else:
            return {"code": 400,"msg": "平台不支持该脚本"}
    
    # python 格式处理
    def python_hand(self,obj,script_tmp):
        result = None
        file_base = f"script.{self.data['name']}"
        if platform.system() == "Windows":
            job_file = str(script_tmp)+"\\"+str(self.data["name"])+".py"
        else:
            job_file = str(script_tmp)+"/"+str(self.data["name"])+".py"
        with open(job_file, "w", encoding='utf8') as f:
            f.write(obj.content)
        cron_str, cron_meta = self.cron_str()
        
        if obj.exec_param :
            if obj.exec_param.startswith("{"):
                job_cron = f"task_method_python, {cron_str},args=['{job_file}', \"{self.job_id}\"],kwargs={eval(obj.exec_param)},id=\"{self.job_id}\""
            elif "," in obj.exec_param:
                exec_param_list = obj.exec_param.split(",")
                job_cron = f"task_method_python,{cron_str},args=['{job_file} {exec_param_list}', \"{self.job_id}\"],id=\"{self.job_id}\""
            else:
                job_cron = f"task_method_python,{cron_str},args=['{job_file} {obj.exec_param}', \"{self.job_id}\"],id=\"{self.job_id}\""
        else:
            job_cron = f"task_method_python,{cron_str}, args=['{job_file}', \"{self.job_id}\"],id=\"{self.job_id}\""

        fc = f"""
django_job=scheduler.add_job({job_cron})
body_data["cron"] = "{cron_meta}"
body_data['job_id'] = django_job.id
body_data["status"] = 0
body_data["job"] = "{obj.name}"
serializer = TaskSerializer(data=body_data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
result = serializer.data
"""
        
        return fc  

    # shell 格式处理
    def shell_hand(self,obj,script_tmp):
        result = None
        if platform.system() == "Windows":
            job_file = str(self.data["name"])+".sh"
        else:
            job_file = str(self.data["name"])+".sh"
        with open(str(script_tmp) + "/" + job_file, "w") as f:
            f.write(obj.content)
        if platform.system() != "Windows":
            os.chmod(str(script_tmp) + "/" + job_file, 0o755)
        hcommand = f"{job_file}"
        if obj.exec_param:
            hcommand = f"{job_file} {obj.exec_param}"
        cron_str,cron_meta = self.cron_str()
        job_cron = f"task_method_shell,{cron_str}, args=[\"{hcommand}\", \"{self.job_id}\"],id=\"{self.job_id}\""        

        fc = f"""
django_job=scheduler.add_job({job_cron})
body_data["cron"] = "{cron_meta}"
body_data['job_id'] = django_job.id
body_data["status"] = 0
body_data["job"] = "{obj.name}"
serializer = TaskSerializer(data=body_data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
result = serializer.data
"""

        return fc

def traverse_directory(path, name):
    for root, dirs, files in os.walk(path):
        for file in files:
            file_path = os.path.join(root, file)
            file_list = file.split(".")
            if file_list[1] == "py":
                type = "python"
            elif file_list[1] == "sh":
                type = "shell"
            else:
                type = "txt"
            if name == file.split(".")[0]:
                return file_path, type

class TaskSerializer(CustomModelSerializer):
    """定时任务 序列化器"""

    class Meta:
        model = Task
        fields = "__all__"


class TaskViewSet(CustomModelViewSet):
    """定时任务"""
    queryset = Task.objects.all()
    serializer_class = TaskSerializer
    search_fields = ('job_id__id', 'status','name')

    def job_list(self, request, *args, **kwargs):
        """获取所有任务的路径"""
        result = get_job_list()
        tasks_list = result.get('tasks_dict_list')
        return SuccessResponse(msg="获取成功", data=tasks_list, total=len(tasks_list))
    

    def create(self, request, *args, **kwargs):
        """创建定时任务"""
        body_data = request.data
        name = body_data.get("name")
        check_name = Task.objects.filter(name=name).first()
        if check_name:
            return ErrorResponse(msg="该任务名称已存在", data=None)
        res = TaskHandler(body_data).task_data()
        if res["code"] == 200:

            exec(res["msg"])
            return SuccessResponse(msg="添加成功", data=res)

        else:
            return ErrorResponse(msg=res["msg"], data=None)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, request=request, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return DetailResponse(data=serializer.data, msg="更新成功")
    
    def get_file(self, request, *args, **kwargs):
        try:
            name = request.GET.get('name')
            script_path = os.path.join(BASE_DIR, "script")
            file_path, file_type = traverse_directory(script_path, name)
            file = open(file_path, "r", encoding='utf-8')
            content = file.read()
            file.close()
            return DetailResponse(data={"type": file_type, "content":content}, msg="更新成功")
        except Exception as e:
            return ErrorResponse(msg=str(e))
    
    def update_file(self, request, *args, **kwargs):
        name = request.data["name"]
        script_path = os.path.join(BASE_DIR, "script")
        if request.data["type"] == "python":
            name = name + ".py"
        elif request.data["type"] == "shell":
            name = name + ".sh"
        if platform.system() == "Windows":
            script_file = str(script_path)+"\\"+str(name)
        else:
            script_file = str(script_path)+"/"+str(name)
        with open(script_file, "w", encoding="utf8") as f:
            f.write(request.data["content"])

        return DetailResponse(data=None, msg="更新成功")

    def destroy(self, request, *args, **kwargs):
        """删除定时任务"""
        name = request.data.pop("name")
        instance = self.get_object()
        self.perform_destroy(instance)
        job_id = instance.job_id_id
        try:
            scheduler.pause_job(job_id)
            scheduler.remove_job(job_id)
            TaskResult.objects.filter(job_id=job_id).delete()
            script_path = os.path.join(BASE_DIR, "script")
            file_path, _ = traverse_directory(script_path, name)
            os.remove(file_path)
        except Exception as e:
            pass
        return SuccessResponse(data=[], msg="删除成功")

    @action(methods=['post'], detail=False)
    def update_status(self, request, *args, **kwargs):
        """开始/暂停任务"""
        
        body_data = request.data
        status = body_data.get('status')
        id = body_data.get('id')
        task_obj = Task.objects.filter(id=id).first()
        job_id = task_obj.job_id_id
        try:
            if status:
                # queried_job = scheduler.get_jobs()
                # for job in queried_job:
                #     print(type(job.id))
                scheduler.pause_job(job_id)
                task_obj.status = 1
                task_obj.save()
            else:
                scheduler.resume_job(job_id)
                task_obj.status = 0
                task_obj.save()
        except Exception as e:
            pass
        return SuccessResponse(msg="修改成功", data=None)
